#!/usr/bin/env python
from __future__ import generators

import tempfile, os, utils
from errors import *
from os3.types.date import Date
Date.default_format = '%Y-%m-%d %H.%M.%S'

class UserManager ( object ):

	# ******************************************************
	# __init__() - costruttore
	#              <filename> specifica il file contenente gli utenti
	# ******************************************************
	def __init__ ( self, filename, pwd_encr_type = 'md5' ):
		super ( UserManager, self ).__init__()
		self._filename = filename
		self._pwd_encr_type = pwd_encr_type


	# ******************************************************
	# get() - recupera i dati di un utente
	#         <user> puo' essere un intero (uid) o una stringa (username)
	# ******************************************************
	def get ( self, user ):
		get_by_name = 0
		if isinstance ( user, str ):
			get_by_name = 1
			user = user.lower()
		for u in self._iter_user_file():
			if (get_by_name and u[0] == user) or u[2] == str(user):
				return self._make_user_dict ( u )


	# ******************************************************
	# users() - restituisce la lista degli utenti
	# ******************************************************
	def users ( self ):
		retval = []
		for l in file ( self._filename ):
			l = l.strip()
			if not l or l[0] == '#': continue
			l = l.split ( ':' )
			retval.append ( self._make_user_dict ( l ) )
		return retval
		

	# ******************************************************
	# add() - aggiunge un utente
	#         <user> e' un dizionario di attributi
	# ******************************************************
	def add ( self, user ):
		uid = user.get ( "uid" )
		if uid is not None:
			user = get ( uid )
			if user: raise UserAlreadyExistsError
				
		username = user [ "username" ]
		username = username.strip().lower()
		if not username: raise InvalidUserNameError
		
		u = self.get ( username )
		if u: raise UserAlreadyExistsError

		user["username"] = username
		user["creation_date"] = Date()
		user["password"] = utils.encrypt_pwd ( user.get ( "password", "" ), self._pwd_encr_type )
		
		user = self._make_user_tuple ( user )

		f = file ( self._filename, "a+" )
		f.write ( ":".join ( user ) + "\n" )
		f.close()


	# ******************************************************
	# delete() - cancella un utente
	#            <user> puo' essere un intero (uid) o una stringa (username)
	# ******************************************************
	def delete ( self, user ):
		user = self.get ( user )
		if not user: raise UserNotFoundError
		
		fh, fname = tempfile.mkstemp()
		os.close ( fh )
		newf = file ( fname, "w" )

		for l in file ( self._filename ):
			if l.split ( ':', 1 )[0] == user["username"]: continue
			newf.write ( l )
		
		newf.close()

		os.unlink ( self._filename )
		os.rename ( fname, self._filename )


	# ******************************************************
	# modify() - modifica uno o piu' attributi di un utente
	#            <user> puo' essere un intero (uid) o una stringa (username)
	#            <attrs> e' il dizionario di attributi
	# ******************************************************
	def modify ( self, user, attrs ):
		user = self.get ( user )
		if not user: raise UserNotFoundError

		for k in attrs:
			user[k] = attrs[k]

		attrs = self._make_user_tuple ( user )

		fh, fname = tempfile.mkstemp()
		os.close ( fh )
		newf = file ( fname, "w" )

		for l in file ( self._filename ):
			if l.split ( ':', 1 )[0] == user["username"]:
				newf.write ( ':'.join ( attrs ) + "\n" )
			else:
				newf.write ( l )
		
		newf.close()

		os.unlink ( self._filename )
		os.rename ( fname, self._filename )

		
	# ******************************************************
	# change_pwd() - modifica la password di un utente
	#            <user> puo' essere un intero (uid) o una stringa (username)
	#            <passwd> e' la nuova password
	# ******************************************************
	def change_pwd ( self, user, passwd ):
		udict = self.get ( user )
		if not udict: raise UserNotFoundError

		udict["password"] = utils.encrypt_pwd ( passwd, self._pwd_encr_type )

		self.modify ( user, udict )


	####################################################
	# INTERNAL METHODS
	####################################################

	def _iter_user_file ( self ):
		for l in file ( self._filename ):
			l = l.strip()
			if not l or l[0] == '#': continue
			yield l.split ( ':' )


	def _make_user_dict ( self, user ):
		return {
			"username": user[0],
			"password": user[1],
			"uid": self._make_int(user[2]),
			"gid": self._make_int(user[3]),
			"last_log": self._make_date(user[4]),
			"creation_date": self._make_date(user[5]),
			"enabled": user[6] and 1 or 0,
			"exp_date": self._make_date(user[7]),
		}


	def _make_user_tuple ( self, user ):
		username = user["username"]
		password = user.get ( "password" ) or ""
		
		uid = user.get ( "uid" )
		if uid == None: uid = ""
		else: uid = str ( uid )
		
		gid = user.get ( "gid" )
		if gid == None: gid = ""
		else: gid = str ( gid )
		
		last_log = str ( user.get ( "last_log" ) or "" )
		creation_date = str ( user.get ( "creation_date" ) )
		exp_date = str ( user.get ( "exp_date" ) or "" )
		
		enabled = str ( user.get ( "enabled", 0 ) )
		
		return username, password, uid, gid, last_log, creation_date, enabled, exp_date
			

	def _make_int ( self, i ):
		try: i = int(i)
		except: i = 0
		return i


	def _make_date ( self, d ):
		if not d: return None
		return Date ( d )

